
/* #######################################################
   #################TRABALHO DE ICC2######################
   #######################################################

Autores: -> Matheus Amato Colussi - Número USP: 6792242
         -> Rafael Mira - Número USP: 4197025

   #######################################################*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef union busca
{
    float salario;
    char nome[64];
}RegBusca;

typedef struct Registro_Pessoa
{
    char nome[64];
    int idade;
    float salario;
    char sexo;
}Registro;

typedef enum {nome, salario} Opcao;

/*Funcao que irá ler do arquivo fornecido as informações necessárias
Entrada:
        P1: Ponteiro para ponteiro do tipo Registro (para mudar o endereço para apontar o vetor com as structs)
        P2: Ponteiro para ponteiro do tipo RegBusca (para mudar o endereço para apontar o vetor com as chaves de buscas)
        P3: String com o caminho do arquivo que será lido
        P4: Ponteiro para o número total de registros
        P5: Ponteiro para o número total de chaves de busca
        P6: Ponteiro para a opção desejada (ref enum Opcao
        P7: Ponteiro para flag de erro
Saída: Endereço do vetor com os registros e endereço do vetor com as chaves de busca, além dos valores alterados por ponteiro de nregistros, nbuscas, e da opção desejada
*/
void acaoLerArquivo(Registro **vetorRegistro, RegBusca **vetorBusca, char caminho[],int *nregistros, int *nbuscas, Opcao *opcao, int *erro)
{
    int i;
    FILE *arq;
    char buffercaminho[500]; /*Variavel que recebera o caminho para ser passado para o fopen*/
    Registro *vetorRegistroAUX; /* Ponteiros auxiliares para passar depois o ponteiro destes para fora da funçăo */
    RegBusca *vetorBuscaAUX;
    sprintf(buffercaminho, "%s", caminho);
    arq = fopen(buffercaminho, "r");
    if (!arq)
    {
        *erro = 1;
    }
    else
    {
        fscanf(arq,"%d", nregistros);

        vetorRegistroAUX = (Registro*)malloc(*nregistros*sizeof(Registro));

        for (i=0;i<*nregistros;i++)
        {
            fscanf(arq,"\n%[^;];%d;%f;%c",&vetorRegistroAUX[i].nome,&vetorRegistroAUX[i].idade,&vetorRegistroAUX[i].salario,&vetorRegistroAUX[i].sexo);

        }

        fscanf(arq,"%d %d", nbuscas, opcao );

        vetorBuscaAUX = (RegBusca*)malloc(*nbuscas*sizeof(RegBusca));

        for (i=0;i<*nbuscas;i++)
        {
            if (*opcao == nome)
            {
                fscanf(arq,"\n%[^\n]", vetorBuscaAUX[i].nome);
            }
            else if (*opcao == salario)
            {
                fscanf(arq,"\n%f", &vetorBuscaAUX[i].salario);
            }
            *erro=0;
        }
    }
    fclose(arq);
    *vetorRegistro = vetorRegistroAUX; /*Passa o ponteiro vetorRegistroAUX para o ponteiro de ponteiro vetorRegistro (para passar os vetores para fora da funçăo */
    *vetorBusca = vetorBuscaAUX; /*Passa o ponteiro vetorBuscaAUX para o ponteiro de ponteiro vetorBusca (para passar os vetores para fora da funçăo */
}
/*Funcao para escrever o registro no arquivo
Entrada:
        P1: Ponteiro para o vetor de Registros
        P2: Posicao do registro a ser escrito no vetor
        P3: Número de registros para ser usado no nome do arquivo de escrita
        P4: Opcao desejada, também para ser usada no nome do arquivo a ser escrito
        P5: O tipo de busca a ser feita - sequencial(0) ou binária(1) *Não está em ENUM
        P6: Flag para saber se achou ou não o registro, caso não ache ele imprime nao encontrado
        P7: Ponteiro para a flag erro
Saída: Ele escreve no arquivo o registro ou a string "nao encontrado"
*/
void acaoEscreveArquivoRegistro(Registro *vetorRegistro, int posicao, int nregistros, Opcao opcao, int tipobusca, int achou, int *erro)
{
    FILE *arqescrita;

    char buffercaminho[500]; /*Cria o buffer para o nome do arquivo que sera escrito */
    switch (tipobusca) /*Verifica qual o tipo de busca para nomear o arquivo, se for sequencial(0) ou binaria(1) */
    {
    case 0:/*Simplesmente para adequar o caminho do arquivo que será escrito */
        if (opcao == nome)
        {
            sprintf(buffercaminho, "sequencial_nomes_%d.txt", nregistros);/* nregistros pois o nome do arquivo tem a ver com o numero de registros que ele possui */
            break;
        }
        else
        {
            sprintf(buffercaminho, "sequencial_salarios_%d.txt", nregistros);/* nregistros pois o nome do arquivo tem a ver com o numero de registros que ele possui */
            break;
        }

    case 1:
        if (opcao == nome)
        {
            sprintf(buffercaminho, "binaria_nomes_%d.txt", nregistros);
            break;
        }
        else
        {
            sprintf(buffercaminho, "binaria_salarios_%d.txt", nregistros);
            break;
        }
    }
    /*Abre o arquivo */
    arqescrita = fopen(buffercaminho, "a");
    if (!arqescrita) *erro = 1;
    /*Se foi passado como parametro achou = 0, ele imprime nao encontrado, caso contrario ele imprime a struct */
    else
    {
        if (!achou) fprintf(arqescrita, "nao encontrado\n");
        else
        {
            fprintf(arqescrita, "%s;%d;%.2f;%c\n", vetorRegistro[posicao].nome, vetorRegistro[posicao].idade, vetorRegistro[posicao].salario, vetorRegistro[posicao].sexo);
        }
        *erro = 0;
    }

    fclose(arqescrita);
}

/*Funcao para escrever a média de comparações no arquivo
Entrada:
        P1: Ponteiro para a média a ser escrita no arquivo
        P2: Número de registros (para o nome do arquivo)
        P3: Enum da opcao, também para o nome do arquivo de escrita
        P4: O tipo da busca, se é sequencial ou binaria (0,1)
        P5: Ponteiro para a flag de erro
Saída: é escrito o número médio de comparações no arquivo.
*/
void acaoEscreveArquivoComparacoes(float *media, int nregistros, Opcao opcao, int tipobusca, int *erro)
{
    FILE *arqescrita;

    char buffercaminho[500]; /*Cria o buffer para o nome do arquivo que sera escrito */
    switch (tipobusca) /*Verifica qual o tipo de busca para nomear o arquivo, se for sequencial(0) ou binaria(1) */
    {
    case 0:/*Simplesmente para adequar o caminho do arquivo que será escrito */
        if (opcao == nome)
        {
            sprintf(buffercaminho, "sequencial_nomes_%d.txt", nregistros);/* nregistros pois o nome do arquivo tem a ver com o numero de registros que ele possui */
            break;
        }
        else
        {
            sprintf(buffercaminho, "sequencial_salarios_%d.txt", nregistros);
            break;
        }

    case 1:
        if (opcao == nome)
        {
            sprintf(buffercaminho, "binaria_nomes_%d.txt", nregistros);
            break;
        }
        else
        {
            sprintf(buffercaminho, "binaria_salarios_%d.txt", nregistros);
            break;
        }
    }

    arqescrita = fopen(buffercaminho, "a");
    if (!arqescrita) *erro = 1; /*Verifica se deu algum erro na hora de abrir o arquivo*/
    else
    {
        fprintf(arqescrita,"%.1f", *media);
        *erro = 0;
    }
    fclose(arqescrita);
}


/*Funcao para realizar a busca binária recursiva
Entrada:
        P1: Ponteiro para um vetor do tipo Registro (vetor aonde será pesquisado os termos do vetor RegBusca)
        P2: Ponteiro para um vetor do tipo RegBusca (vetor que irá conter as chaves de buscas)
        P3: Posição da chave de busca no vetorBusca
        P4: Numero total de registros (argumento para usar na chamada a funcao acaoEscreveRegistro)
        P5: Opção do arquivo: nome(0) ou salário(1)
        P6: Inteiro para o inicio do vetor (ou subvetor) que será pesquisado
        P7: Inteiro para o fim do vetor (ou subvetor) que será pesquisado
        P8: Ponteiro para a variavel contadora ncomparacoes (que é float, pois sera dividida para escrever a média de comparações)
        P9: Flag BOOLEANA para saber se achou ou não (será passada para a função acaoEscreveRegistro)
        P10: Ponteiro para uma flag BOOLEANA de erro (1 caso tenha algum erro, 0, caso contrário)
Saída: 1 se achou, 0, caso contrário
         */

int acaoBuscaBinariaRecursiva(Registro *vetorRegistro, RegBusca *vetorBusca, int i, int nregistros, Opcao opcao, int ini, int fim, float *ncomparacoes, int achou, int *erro)
{
    int k; /*Variável que será a média do vetor */
    k = (ini+fim)/2;
    if (fim>=ini)
    {

        if (opcao == nome)
        {
            *ncomparacoes = *ncomparacoes + 1;
            if (strcmp(vetorBusca[i].nome, vetorRegistro[k].nome) == 0)
            { /*Condicao de PARADA 1 */
                achou = 1; /*Caso seja igual, é setada a flag achou para 1, indicando que achou */

                /*Escreve a struct encontrada no arquivo de texto */
                acaoEscreveArquivoRegistro(vetorRegistro, k, nregistros, opcao, 1, achou, erro);
            }

            else if (strcmp(vetorBusca[i].nome, vetorRegistro[k].nome) > 0) /*Caso o chave de busca seja alfabeticamente maior que o nome na posição k, ele dividi o vetor e busca na segunda metade */
            {
                *ncomparacoes = *ncomparacoes + 1;
                /*Faz a chamada recursiva da função, com a mudanca de variavel na propria chamada (k+1) no inicio*/
                acaoBuscaBinariaRecursiva(vetorRegistro, vetorBusca, i, nregistros, opcao, k+1, fim, ncomparacoes, achou, erro);
            }
            else  /*Caso o chave de busca seja alfabeticamente menor que o nome na posição k, ele dividi o vetor e busca na primeira metade */
            {
                *ncomparacoes = *ncomparacoes + 1;
                /*Faz a chamada recursiva da função, com a mudanca de variavel na propria chamada (k-1) no fim */
                acaoBuscaBinariaRecursiva(vetorRegistro, vetorBusca, i, nregistros, opcao, ini, k-1, ncomparacoes, achou, erro);/*O fim vai para a posicao k-1, para o primeiro subvetor (Processo de busca binária)*/
            }
        }
        else if (opcao == salario)
        {
            *ncomparacoes = *ncomparacoes + 1;
            if (vetorBusca[i].salario == vetorRegistro[k].salario)
            { /* Condicao de PARADA 1 */
                achou = 1;

                /*Escreve a struct encontrada no arquivo de texto */
                acaoEscreveArquivoRegistro(vetorRegistro, k, nregistros, opcao, 1, achou, erro);
                return 1;
            }

            else if (vetorBusca[i].salario > vetorRegistro[k].salario) /*Caso a chave de busca seja maior, usa o subvetor acima de k*/
            {
                *ncomparacoes = *ncomparacoes + 1;
                /*Faz a chamada recursiva da função, com a mudanca de variavel na propria chamada (k+1) no inicio*/
                acaoBuscaBinariaRecursiva(vetorRegistro, vetorBusca, i, nregistros, opcao, k+1, fim, ncomparacoes, achou, erro);
            }
            else /*Caso a chave de busca seja menor, usa o subvetor abaixo de k*/
            {
                *ncomparacoes = *ncomparacoes + 1;
                /*Faz a chamada recursiva da função, com a mudanca de variavel na propria chamada (k-1) no fim */
                acaoBuscaBinariaRecursiva(vetorRegistro, vetorBusca, i, nregistros, opcao, ini, k-1, ncomparacoes, achou, erro);/*O fim vai para a posicao k-1, para o primeiro subvetor (Processo de busca binária)*/
            }

        }
    }
    else
    {
        /* Condicao de parada 2 */
        achou = 0;
        /*Chama a funcao para escrever o arquivo que nao encontrou, no caso (nao encontrado) */
        acaoEscreveArquivoRegistro(vetorRegistro, k, nregistros, opcao, 1, achou, erro);
        return 0;
    }
    return 0;
}
int main()
{
    char caminho[500]; /*Variavel para ser inserido o caminho do arquivo*/
    int nregistros, nbuscas, erro, final = 0, i;/*Variaveis para serem utilizadas no programa, sendo a ultima delas uma flag para saber se terminou o programa ou nao */
    float ncomparacoes = 0; /*Seta e zera ncomparacoes para usar na chamada a funcao busca binaria recursiva */
    Opcao opcao;
    Registro *vetorRegistro; /*Declaracao do ponteiro para o vetorRegistro que sera alocado dinamicamente mais tarde */
    RegBusca *vetorBusca; /*Declaracao do ponteiro para o vetorRegistro que sera alocado dinamicamente mais tarde */
    while (!final) /*Enquanto a flag for fim = 0, continua executando */
    {

        scanf( "%s", caminho); /*Pega o caminho dos arquivos */
        if (strcmp(caminho,"FIM")== 0) /*Compara o caminho com a palavra FIM, para verificar se termina ou nao o programa */
        {
            final = 1;/*Em caso afirmativo, muda a flag */
            continue; /*E pula o resto do codigo ate o while, poderia ser usado o break, mas assim fica mais intuitivo */
        }

        acaoLerArquivo(&vetorRegistro, &vetorBusca, caminho, &nregistros, &nbuscas, &opcao, &erro); /*Funcao para ler o arquivo e retornar duas structs, alem de varias variaveis */
        if (erro == 1) return 0;
        for (i=0;i<=nbuscas;i++)/*Funcao para caminhar pelo vetorBusca para as chaves de buscas, para cada uma delas ele chama a funcao acaoBuscaBinariaRecursiva, que irá fazer a busca */
        { /* Chama a funcao para fazer a busca binária no vetor nregistro de acordo com a chave de busca desejada */
            acaoBuscaBinariaRecursiva(vetorRegistro, vetorBusca, i, nregistros, opcao, 0, nregistros, &ncomparacoes, 0, &erro);
        }
        ncomparacoes = ncomparacoes/nbuscas; /*Dividi o n de comparacoes para obter a média */
        acaoEscreveArquivoComparacoes(&ncomparacoes, nregistros,  opcao, 1, &erro);
        free(vetorBusca);
        free(vetorRegistro);
    }
    return 0;
}


